/*
    PhotoFlip, a free 3d photo managment suite.
    Copyright (C) 2007  Elisée MAURER, Gaétan WALTER
    
    This file is part of PhotoFlip.
    Contact: http://www.photoflip.org/

    PhotoFlip is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    PhotoFlip is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with PhotoFlip.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "dock.hpp"
#include "constants.hpp"

#include <iostream>

dockIcon::dockIcon(): texIcon(0), visible(false) {
	
}

dockIcon::dockIcon(std::string action, GLuint texture, bool visible): action(action), texIcon(texture), visible(visible) {
	
}

// ----------------------------------------------------------------------------
// dock
// ----------------------------------------------------------------------------
dock::dock(library *lib): m_lib(lib), m_height(0), m_risen(false), m_raiseValue(0.0f), m_texDock(0) {
	
}

dock::~dock() {
	
}

void dock::initTexture() {
	glGenTextures(1, &m_texDock);
}

int dock::computeLocationBarWidth() {
	int width = 0;
	
	m_cairoContext->save();
		m_cairoContext->select_font_face("Sans Serif", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD);
		m_cairoContext->set_font_size(m_height / 2.0f);
		for(unsigned int i = 0; i < m_location.size(); i++) {
			Cairo::TextExtents textExtents;
			m_cairoContext->get_text_extents(m_location[i], textExtents);
			width += int(textExtents.x_advance + (0.2f * m_height) + DOCK_OUTLINE);
		}
	m_cairoContext->restore();
	width += int(0.1f * m_height);
	
	return width;
}

int dock::computeBasketWidth() {
	int width = 0;
	
	for(unsigned int i = 0; i < m_lib->getBasket().size(); i++) {
		GLint thumbWidth, thumbHeight;
		
		if(m_lib->getBasket()[i].hasTexture) glBindTexture(GL_TEXTURE_2D, m_lib->getBasket()[i].texPhoto);
		else glBindTexture(GL_TEXTURE_2D, m_lib->getErrorTexture());
		
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &thumbWidth);
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &thumbHeight);
		
		float ratio = float(thumbWidth) / thumbHeight;
		width += int(m_height * (ratio + 0.1));
	}
	
	if(width == 0) return m_height;
	else return width;
}

int dock::computeWidth() {
	int width = 0;
	
	// icons
	for(unsigned int i = 0; i < m_icons.size(); i++)
		if(m_icons[i].visible)
			width += m_height; //! \todo this will break for non-square icons
	// separator
	width += int(0.2 * m_height);
	// location bar width
	width += computeLocationBarWidth();
	// separator
	width += int(0.2 * m_height);
	// basket
	width += computeBasketWidth();
	
	return width;
}

void dock::clearIcons() {
	m_icons.clear();
}

void dock::packIcon(std::string action, GLuint texIcon, bool visible) {
	dockIcon icon(action, texIcon, visible);
	m_icons.push_back(icon);
}

dockIcon *dock::getIcon(std::string action) {
	for(unsigned int i = 0; i < m_icons.size(); i++) {
		if(m_icons[i].action == action) return &m_icons[i];
	}
	
	return NULL;
}

void dock::pushLocation(std::string location) {
	m_location.push_back(location);
}

void dock::popLocation() {
	m_location.pop_back();
}

void dock::clearLocation() {
	m_location.clear();
}

void dock::setSize(int width, int height) {
	m_height = height;
	
	m_cairoSurface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width, height + DOCK_OUTLINE);
	m_cairoContext = Cairo::Context::create(m_cairoSurface);
}

void dock::raise(bool state) {
	m_risen = state;
}

void dock::raise(int x, int y) {
	m_risen = true;
	
	/*int dockWidth = 0;
	for(unsigned int i = 0; i < m_widgets.size(); i++)
		dockWidth += int(m_widgets[i].getWidthFactor() * m_height);
	
	int dockX = m_cairoSurface->get_width() / 2 - dockWidth / 2;
	if((x < dockX) || (x > dockX + dockWidth)) return;
	
	for(unsigned int i = 0; i < m_widgets.size(); i++) {
		m_widgets[i].setZoom(1.0f - fabs(dockX - x) / dockWidth);
		dockX += int(m_widgets[i].getWidthFactor() * m_height);
	}*/
}

// NEEDS UPDATING
std::string dock::getAction(int x) {
	int locationBarWidth = computeLocationBarWidth(),
		basketWidth = computeBasketWidth();
	
	int width = 0;
	
	// icons
	for(unsigned int i = 0; i < m_icons.size(); i++)
		if(m_icons[i].visible)
			width += m_height; //! \todo this will break for non-square icons
	// separator
	width += int(0.2 * m_height);
	// location bar width
	width += locationBarWidth;
	// separator
	width += int(0.2 * m_height);
	// basket
	width += basketWidth;
	
	// look for an icon
	int dx = m_cairoSurface->get_width() / 2 - width / 2;
	if(x < dx) return "";
	
	for(unsigned int i = 0; i < m_icons.size(); i++) {
		if(!m_icons[i].visible) continue;
		
		dx += m_height; //! \todo this will break for non-square icons
		if(x <= dx) return m_icons[i].action;
	}
	
	dx += int(0.2 * m_height); // separator
	if(x <= dx) return "";
	
	// location bar?
	dx += locationBarWidth;
	dx += int(0.1f * m_height);
	if(x <= dx) return "location-bar";
	
	dx += int(0.2 * m_height); // separator
	if(x <= dx) return "";
	
	// basket?
	dx += basketWidth;
	if(x <= dx) return "basket";
	
	return "";
}

int dock::getBasketPhoto(int x) {
	int iconsWidth = 0,
		locationBarWidth = computeLocationBarWidth(),
		basketWidth = computeBasketWidth(),
		width = 0;
	
	// icons
	for(unsigned int i = 0; i < m_icons.size(); i++)
		if(m_icons[i].visible)
			iconsWidth += m_height; //! \todo this will break for non-square icons
	width += iconsWidth;
	// separator
	width += int(0.2 * m_height);
	// location bar width
	width += locationBarWidth;
	// separator
	width += int(0.2 * m_height);
	// basket
	width += basketWidth;
	
	int dx = m_cairoSurface->get_width() / 2 - width / 2;
	dx += iconsWidth + int(0.2 * m_height) + locationBarWidth + int(0.2 * m_height);
	
	if(x < dx) return -1;
	
	for(unsigned int i = 0; i < m_lib->getBasket().size(); i++) {
		if(m_lib->getBasket()[i].hasTexture) glBindTexture(GL_TEXTURE_2D, m_lib->getBasket()[i].texPhoto);
		else glBindTexture(GL_TEXTURE_2D, m_lib->getErrorTexture());
		
		GLint width, height;
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
		
		float ratio = float(width) / height;
		
		dx += int(m_height * ratio);
		if(x < dx) return i;
		dx += int(m_height * 0.1);
	}
	
	return -1;
}

bool dock::update() {
	bool next = false;
	
	if(m_risen) {
		if(m_raiseValue + fabs(m_raiseValue - 100.0f) / 5.0f + 1.0f < 100.0f) {
			m_raiseValue += fabs(m_raiseValue - 100.0f) / 5.0f;
			next = true;
		} else {
			m_raiseValue = 100.0f;
		}
	} else {
		if(m_raiseValue - m_raiseValue / 5.0f - 1.0f > 0.0f) {
			m_raiseValue -= m_raiseValue / 5.0f;
			next = true;
		} else m_raiseValue = 0.0f;
	}
	
	return next;
}

void dock::render() {
	// compute dock width
	int width = computeWidth();
		
	float dockLeft = m_cairoSurface->get_width() / 2 - (width / 2.0f) - DOCK_OUTLINE / 2.0f;
	float dockWidth = width + DOCK_OUTLINE;
	
	float dockTop = DOCK_OUTLINE / 2.0f;
	float dockHeight = m_height + DOCK_OUTLINE / 2.0f;
	
	// draw dock
	m_cairoContext->save();
		m_cairoContext->set_operator(Cairo::OPERATOR_SOURCE);
		m_cairoContext->set_source_rgba(0.00, 0.00, 0.00, 0.00);
		m_cairoContext->paint();
	m_cairoContext->restore();
	
	m_cairoContext->save();
		m_cairoContext->move_to(dockLeft, dockTop + dockHeight);
		
		m_cairoContext->line_to(dockLeft, dockTop + DOCK_ROUNDING);
		m_cairoContext->curve_to(dockLeft, dockTop, dockLeft, dockTop, dockLeft + DOCK_ROUNDING, dockTop);
		
		m_cairoContext->line_to(dockLeft + dockWidth - DOCK_ROUNDING, dockTop);
		m_cairoContext->curve_to(dockLeft + dockWidth, dockTop, dockLeft + dockWidth, dockTop, dockLeft + dockWidth, dockTop + DOCK_ROUNDING);
		
		m_cairoContext->line_to(dockLeft + dockWidth, dockTop + dockHeight);
		
		m_cairoContext->set_source_rgba(TANGO_COLOR_ALUMINIUM2_DARK, 0.8f);
		m_cairoContext->fill_preserve();
		m_cairoContext->set_line_width(DOCK_OUTLINE);
		m_cairoContext->set_source_rgba(TANGO_COLOR_ALUMINIUM2_LIGHT, 0.5f);
		m_cairoContext->stroke();
	m_cairoContext->restore();
	
	// draw widgets
	int x = m_cairoSurface->get_width() / 2 - width / 2;
	
	// (icons)
	for(unsigned int i = 0; i < m_icons.size(); i++)
		if(m_icons[i].visible)
			x += m_height;
	
	// (separator)
	m_cairoContext->save();
		m_cairoContext->move_to(x + m_height * 0.1f, int(m_height * 0.1f) + DOCK_OUTLINE / 2.0f);
		m_cairoContext->rel_line_to(0, m_height * 0.8f);
		
		m_cairoContext->set_line_cap(Cairo::LINE_CAP_ROUND);
		m_cairoContext->set_line_width(DOCK_OUTLINE / 2.0f);
		m_cairoContext->set_source_rgba(TANGO_COLOR_ALUMINIUM2_LIGHT, 0.2f);
		m_cairoContext->stroke();
	m_cairoContext->restore();
	
	x += int(0.2f * m_height);

	// (location bar)
	for(unsigned int i = 0; i < m_location.size(); i++) {
		Cairo::TextExtents textExtents;
		
		m_cairoContext->save();
			m_cairoContext->select_font_face("Sans Serif", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD);
			m_cairoContext->set_font_size(m_height / 2.0f);
			m_cairoContext->get_text_extents(m_location[i], textExtents);
			
			m_cairoContext->move_to(x + m_height * 0.1f, int(m_height * 0.1f) + DOCK_OUTLINE / 2.0f);
			m_cairoContext->rel_line_to(textExtents.x_advance + m_height * 0.1f + DOCK_OUTLINE, 0);
			m_cairoContext->rel_line_to(0, m_height * 0.8f);
			m_cairoContext->rel_line_to(-(textExtents.x_advance + m_height * 0.1f + DOCK_OUTLINE), 0);
			m_cairoContext->close_path();
			
			m_cairoContext->set_source_rgba(TANGO_COLOR_ALUMINIUM2_DARK, 0.8f);
			m_cairoContext->fill_preserve();
			m_cairoContext->set_line_width(DOCK_OUTLINE / 2.0f);
			m_cairoContext->set_source_rgba(TANGO_COLOR_ALUMINIUM2_LIGHT, 0.5f);
			m_cairoContext->stroke();
			
			m_cairoContext->move_to(x + m_height * 0.1f + DOCK_OUTLINE, int(m_height / 2.0f + m_height * 0.1f + DOCK_OUTLINE * 2.0f));
			m_cairoContext->show_text(m_location[i]);
		m_cairoContext->restore();
		
		x += int(textExtents.x_advance + (0.2f * m_height) + DOCK_OUTLINE);
	}
	
	x += int(0.1f * m_height);
	
	//x += 5 * m_height;
	
	// (separator)
	m_cairoContext->save();
		m_cairoContext->move_to(x + m_height * 0.1f, int(m_height * 0.1f) + DOCK_OUTLINE / 2.0f);
		m_cairoContext->rel_line_to(0, m_height * 0.8f);
		
		m_cairoContext->set_line_cap(Cairo::LINE_CAP_ROUND);
		m_cairoContext->set_line_width(DOCK_OUTLINE / 2.0f);
		m_cairoContext->set_source_rgba(TANGO_COLOR_ALUMINIUM2_LIGHT, 0.2f);
		m_cairoContext->stroke();
	m_cairoContext->restore();
	
	x += int(0.2f * m_height);
	
	// (basket)
	int basketLocation = x;
	
	// generate final texture
	glBindTexture(GL_TEXTURE_2D, m_texDock);
	
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
	
	// NOTE: is it endian-ness dependent?
#ifdef WIN32
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_cairoSurface->get_width(),
		m_cairoSurface->get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
		m_cairoSurface->get_data());
#else
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_cairoSurface->get_width(),
		m_cairoSurface->get_height(), 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
		m_cairoSurface->get_data());
#endif
	
	glPushMatrix();
		glTranslatef(0, m_cairoSurface->get_height() * m_raiseValue / 100.0f, 0);
		glBegin(GL_QUADS);
			glTexCoord2d(0, 1);
			glVertex2f(0, -m_cairoSurface->get_height());
			glTexCoord2d(1, 1);
			glVertex2f(m_cairoSurface->get_width(), -m_cairoSurface->get_height());
			glTexCoord2d(1, 0);
			glVertex2f(m_cairoSurface->get_width(), 0);
			glTexCoord2d(0, 0);
			glVertex2f(0, 0);
		glEnd();
	glPopMatrix();
	
	// draw icons
	x = m_cairoSurface->get_width() / 2 - width / 2;
	for(unsigned int i = 0; i < m_icons.size(); i++) {
		if(!m_icons[i].visible) continue;
		
		glBindTexture(GL_TEXTURE_2D, m_icons[i].texIcon);
		glPushMatrix();
			glTranslatef(x, int(m_height * m_raiseValue / 100.0f - DOCK_OUTLINE / 2.0f), 0);
			glBegin(GL_QUADS);
				glTexCoord2d(0, 1);
				glVertex2f(0, -m_height /* * m_icons[i].zoom */);
				glTexCoord2d(1, 1);
				glVertex2f(m_height /* * m_icons[i].zoom */, -m_height /* * m_icons[i].zoom */);
				glTexCoord2d(1, 0);
				glVertex2f(m_height /* * m_icons[i].zoom */, 0);
				glTexCoord2d(0, 0);
				glVertex2f(0, 0);
			glEnd();
		glPopMatrix();
		
		x += m_height;
	}
	
	// draw basket
	x = basketLocation;
	for(unsigned int i = 0; i < m_lib->getBasket().size(); i++) {
		if(m_lib->getBasket()[i].hasTexture) glBindTexture(GL_TEXTURE_2D, m_lib->getBasket()[i].texPhoto);
		else glBindTexture(GL_TEXTURE_2D, m_lib->getErrorTexture());
		
		GLint width, height;
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
		
		float ratio = float(width) / height;
		
		glPushMatrix();
			glTranslatef(x, int(m_height * m_raiseValue / 100.0f - DOCK_OUTLINE / 2.0f), 0);
			
			glBegin(GL_QUADS);
				glTexCoord2d(0, 1);
				glVertex2f(0, -m_height);
				glTexCoord2d(1, 1);
				glVertex2f(m_height * ratio, -m_height);
				glTexCoord2d(1, 0);
				glVertex2f(m_height * ratio, 0);
				glTexCoord2d(0, 0);
				glVertex2f(0, 0);
			glEnd();
		
		glPopMatrix();
		
		x += int(m_height * (ratio + 0.1));
	}
}
